package plp.orientadaObjetos2.memoria;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.VariavelNaoDeclaradaException;
import plp.orientadaObjetos1.expressao.leftExpression.Id;
import plp.orientadaObjetos1.memoria.ContextoCompilacaoOO1;
import plp.orientadaObjetos1.memoria.colecao.ListaValor;
import plp.orientadaObjetos1.util.Tipo;
import plp.orientadaObjetos2.declaracao.interfaces.DefInterface;
import plp.orientadaObjetos2.excecao.interfaces.InterfaceJaDeclaradaException;
import plp.orientadaObjetos2.excecao.interfaces.InterfaceNaoDeclaradaException;
import plp.orientadaObjetos2.util.SuperClasseMap;

public class ContextoCompilacaoOO2 extends ContextoCompilacaoOO1 implements AmbienteCompilacaoOO2 {

	private ArrayList<SuperClasseMap> arraySuperClasse;
	
	/**
     * A pilha de classes do contexto.
     */
    private Stack<HashMap<Id, DefInterface>> pilhaDefInterface;

	public ContextoCompilacaoOO2(ListaValor entrada) {
		super(entrada);		
		arraySuperClasse = new ArrayList <SuperClasseMap> ();
		pilhaDefInterface = new Stack<HashMap<Id, DefInterface>> ();
		pilhaDefInterface.push(new HashMap<Id, DefInterface>()); 
	}

	/**
	 * Mapeia o id da sub-classe em uma super-classe.
	 */
	@SuppressWarnings("unchecked")
	public void mapSuperClasse(Id classe, ArrayList superClasse)
	throws ClasseNaoDeclaradaException {
		for(int i=0; i < superClasse.size(); i++){
			System.out.println(((Id) superClasse.get(i)).toString());
			arraySuperClasse.add(
					new SuperClasseMap( classe, (Id) superClasse.get(i) )
			);
		}
	}

	/**
	 * Dado o id de uma classe, 
	 * recupera a definicao da super-classe. 
	 */
	public ArrayList<SuperClasseMap> getSuperClasse(Id classe)
	throws ClasseNaoDeclaradaException {
		ArrayList<SuperClasseMap> arrayAux = new ArrayList<SuperClasseMap>();
		for(int i=0; i < arraySuperClasse.size(); i++){
			String nomeClasse = arraySuperClasse.get(i).getClasse().toString();
			if(nomeClasse.equalsIgnoreCase( classe.toString() ))
				arrayAux.add( arraySuperClasse.get(i) );
		}
		return arrayAux;
	}

	@Override
	public Tipo getTipoEntrada() throws VariavelNaoDeclaradaException {
		Tipo aux =  getEntrada().getHead().getTipo(this);
		setEntrada( (ListaValor)getEntrada().getTail() );
		return aux;
	}

	public void mapDefInterface(Id idInterface, DefInterface defInterface) throws InterfaceJaDeclaradaException {
		HashMap<Id, DefInterface> aux = pilhaDefInterface.peek();
        if (aux.put(idInterface, defInterface) != null) {
            throw new InterfaceJaDeclaradaException(idInterface);
        }
	}

	public DefInterface getDefInterface(Id idInterface) throws InterfaceNaoDeclaradaException {
		
		DefInterface result = null;
        Stack<HashMap<Id, DefInterface>> auxStack = new Stack<HashMap<Id, DefInterface>>();
        while (result == null && !pilhaDefInterface.empty()) {
            HashMap<Id, DefInterface> aux = pilhaDefInterface.pop();
            auxStack.push(aux);
            result = aux.get(idInterface);
        }
        while (!auxStack.empty()) {
        	pilhaDefInterface.push(auxStack.pop());
        }
        if (result == null) {
            throw new InterfaceNaoDeclaradaException(idInterface);
        } else {
            return result;
        }
        
	}

}